package com.niknik.woody.utils.wunit.junit.rule;

import com.google.inject.Inject;
import com.niknik.woody.utils.WSchema;
import com.niknik.woody.utils.WUtils;
import org.junit.rules.ExternalResource;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;

import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.List;
import java.util.logging.*;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;

@Deprecated
public class LogMonitor
    extends ExternalResource {

  @Inject private Handler logHandlerMock;
  @Inject private InOrder inOrder;

  private Integer nextLogsIdx;
  private List<LogRecord> logs;

  static {
    String confFile = "/com/niknik/woody/logging.properties";
    final InputStream in = LogMonitor.class.getResourceAsStream(confFile);
    try {
      LogManager.getLogManager().readConfiguration(in);
    }
    catch (IOException e) {
      Logger.getAnonymousLogger().log(Level.SEVERE, "Failed to read logging.properties for tests.", e);
    }
  }

  public void count(int logsCount) {
    nextLogsIdx = 0;
    ArgumentCaptor<LogRecord> logRecordCaptor = ArgumentCaptor.forClass(LogRecord.class);
    inOrder.verify(logHandlerMock, times(logsCount)).publish(logRecordCaptor.capture());
    logs = logRecordCaptor.getAllValues();
  }

  public void methodCall(Class<?> logClazz, String msg, String... params) {
    assertLog(logClazz, null, msg, params, Level.FINEST);
  }

  public void exThrow(Class<?> logClazz, Class<?> exClazz, String msg, String... params) {
    assertLog(logClazz, exClazz, msg, params, Level.FINER);
  }

  public void errCatch(Class<?> logClazz, Class<?> exClazz, String msg, String... params) {
    assertLog(logClazz, exClazz, msg, params, Level.FINE);
  }

  public void fatalCatch(Class<?> logClazz, Class<?> exClazz, String msg, String... params) {
    assertLog(logClazz, exClazz, msg, params, Level.SEVERE);
  }

  private void assertLog(Class<?> logClazz, Class<?> exClazz, String msg, Object[] params, Level level) {
    if (null == logs) {
      throw new IllegalStateException("You should invoke .count() method before checking each log record.");
    }
    LogRecord record = logs.get(nextLogsIdx);
    nextLogsIdx++;

    params = WUtils.firstNotNull(params, new Object[]{null});
    String formattedMsg = msg == null
        ? null
        : MessageFormat.format(msg, params);

    assertThat(record.getLevel(), is(level));
    assertThat(record.getLoggerName(), is(logClazz.getName()));
    if (null != formattedMsg) {
      assertThat(record.getMessage(), is(formattedMsg));
    }

    assertThrown(exClazz, formattedMsg, record.getThrown());
  }

  private void assertThrown(Class<?> exClazz, String msg, Throwable th) {
    if (null != exClazz) {
      assertNotNull(th);
      assertTrue(exClazz.isInstance(th));
    }
  }

  @Override
  protected void before()
      throws Throwable {
    super.before();
    reset(logHandlerMock);
    Logger.getLogger(WSchema.NIKNIK_NAMESPACE).addHandler(logHandlerMock);
  }

  @Override
  protected void after() {
    super.after();
    Logger.getLogger(WSchema.NIKNIK_NAMESPACE).removeHandler(logHandlerMock);
  }
}
